package calculators;

import algorithm.AlgorithmParameters;
import herd.Krill;
import utils.NeighboursDeterminator;
import utils.Position;
import utils.PositionMath;

import java.util.List;
import java.util.Random;

public class AlphaCalculator {
    private final TendencyCalculator tendencyCalculator = new TendencyCalculator();
    private final PositionMath positionMath = new PositionMath();
    private final NeighboursDeterminator neighboursDeterminator = new NeighboursDeterminator();
    private final Random random = new Random(44);

    public Position calculateAlpha(List<Krill> herd, Krill krill, AlgorithmParameters parameters) {
        List<Krill> neighbours = neighboursDeterminator.determinateNeighbours(herd, krill);
        Position localAlpha = calculateAlphaLocal(neighbours, krill, parameters);
        Position targetAlpha = calculateAlphaTarget(parameters,krill);

        return positionMath.addPositions(localAlpha,targetAlpha);
    }

    private Position calculateAlphaLocal(List<Krill> neighbours, Krill krill, AlgorithmParameters parameters) {
        Position alphaLocal = new Position(0.0, 0.0);

        for (Krill neighbour : neighbours) {
            Position relatedPosition = tendencyCalculator.calculateRelatedPosition(neighbour.getPosition(), krill.getPosition());
            double relatedFitness = tendencyCalculator.calculateRelatedFitness(neighbour.getFitnessValue(), krill.getFitnessValue(), parameters);

            positionMath.multiplyPositionImplicitly(relatedPosition, relatedFitness);
            positionMath.addPositionsImplicitly(alphaLocal, relatedPosition);
        }
        return alphaLocal;
    }

    private Position calculateAlphaTarget(AlgorithmParameters parameters, Krill krill) {
        Position relatedPosition = tendencyCalculator.calculateRelatedPosition(parameters.getBestFitnessKrill().getPosition(), krill.getPosition());
        double relatedFitness = tendencyCalculator.calculateRelatedFitness(parameters.getBestFitnessKrill().getFitnessValue(), krill.getFitnessValue(), parameters);

        positionMath.multiplyPositionImplicitly(relatedPosition,relatedFitness);
        positionMath.multiplyPositionImplicitly(relatedPosition,calculateCoefficient(parameters));

        return relatedPosition;
    }

    private double calculateCoefficient(AlgorithmParameters parameters) {
        return 2.0 * (random.nextDouble() + parameters.getIterationRatio());
    }
}
